home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
AMIGA
/
(A)U
/
(A)U4.ADF
/
Parsnag
/
parsnag.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-07-20
|
6KB
|
179 lines
/* parsnag.c - (c) 1987 Dallas John Hodgson
The purpose of this program is to intercept all write commands
to the parallel.device, and route the data to a disk file. Besides being
an interesting tutorial, this program works wonders for color print dumps!
Entire dithered print grabs can be stored on disk for later use, and
fast-printed by copying to PAR. Great way to benchmark printer (as opposed to
printer driver) graphic throughput. Color separations could be even be
performed on the resulting output; desktop color publishing is here!
HOW IT WORKS
============
It helps to think of devices as libraries with only a few standardized
functions; these include Open, Close, Expunge, BeginIO, AbortIO, &
user-defined. As with any other library, these functions are negative-indexed
into a jump table off its base address. What makes devices unique is that :
a) OpenDevice returns more than just an OpenLibrary() base pointer
b) Most commands are processed through one subroutine (BeginIO)
acting as command-handler instead of through separate entries.
c) Devices commonly spawn tasks for asynchronous dirty work, in
which case they are expected to reply when done.
This program replaces the BeginIO vector of the device in question and
substitutes a 68K handler that does one of two things; if the command requested
of BeginIO is other than a CMD_WRITE, control is transfered to the original
vector. If it is a CMD_WRITE, control is transfered to our handler which writes
the buffer to a disk file & replies to the caller.
There is a lot of information-hiding going inside Exec; DoIO() may act
synchronous, but internally it operates like so :
1) Set the IOF_QUICK flag (politely request synchronous IO)
2) Call BeginIO()
3) Check to see if IOF_QUICK still set; if so, the routine really
performed synchronously, and we can return.
4) Otherwise, Wait() for a reply message then return.
SendIO() resets the _QUICK bit and calls BeginIO().
OpenDevice(), CloseDevice(), & AbortIO() all call the appropriate
vectors off a user supplied base pointer as opposed to a statically-defined
pointer like IntuitionBase. This way, all I/O routines can work with all
devices.
By the way; the Exec manual was NO help figuring out this stuff.
Metascoping through KickStart was, however. Hmm, maybe this is how those
'floppy cache' programs are implemented! Hmm...
*/
extern void ParDispatch(),ParExpunge();
long OldExpunge,OldBeginIO; /* referenced by par.asm */
#define BUFNAME "ram:parfile"
/* most common device functions; first 4 should ALWAYS exist */
#define IO_OPEN (-6)
#define IO_CLOSE (-12)
#define IO_EXPUNGE (-18)
#define IO_EXTFUNC (-24)
#define IO_BEGIN (-30)
#define IO_ABORT (-36)
void *IntuitionBase;
struct MsgPort *txport;
struct NewWindow nw;
main()
{
struct IOExtPar *ioblock=0;
struct Device *deventry;
struct MsgPort *rport=0;
struct Window *window=0;
nw.Width=320; nw.Height=10;
nw.DetailPen=nw.BlockPen=-1;
nw.IDCMPFlags=CLOSEWINDOW;
nw.Flags=WINDOWDEPTH|WINDOWDRAG|WINDOWCLOSE;
nw.Title=(UBYTE *)"ParSnag (c) 1987 John Hodgson";
nw.Type=WBENCHSCREEN;
IntuitionBase=OpenLibrary("intuition.library",0);
if (!(window=OpenWindow(&nw))) goto cleanup;
if (!(rport=CreatePort(0,0))) goto cleanup;
if (!(txport=CreatePort(0,0))) goto cleanup;
if (!(ioblock=(struct IOExtPar *)CreateExtIO(rport,sizeof(*ioblock))))
goto cleanup;
if (OpenDevice("parallel.device",0,ioblock,0)) goto cleanup;
deventry=ioblock->IOPar.io_Device;
/* misc note : Some Programs (DPaint II) may completely expunge its devices
in order to reclaim memory. We don't want this to happen, since an
expunged parallel.device will load in the second time around at a
different location and WITHOUT our sneaky little patches! */
OldBeginIO=SetFunction(deventry,IO_BEGIN,ParDispatch);
OldExpunge=SetFunction(deventry,IO_EXPUNGE,ParExpunge); /* dummy expunge */
CloseDevice(ioblock); /* free so others can use */
ParFlush(window); /* blocks until CLOSEWINDOW */
SetFunction(deventry,IO_BEGIN,OldBeginIO); /* restore the damage */
SetFunction(deventry,IO_EXPUNGE,OldExpunge);
cleanup:
if (window) CloseWindow(window);
if (rport) DeletePort(rport);
if (txport) DeletePort(txport);
if (ioblock) DeleteExtIO(ioblock,sizeof(*ioblock));
CloseLibrary(IntuitionBase);
}
ParFlush(window) /* par: to disk handler */
struct Window *window;
{
struct FileHandle *fp;
struct IOExtPar *iomsg;
long result;
for (;;) {
result=Wait(1<<txport->mp_SigBit | 1<<window->UserPort->mp_SigBit);
if (result & 1<<window->UserPort->mp_SigBit) return; /* back if close */
iomsg=(struct IOExtPar *)GetMsg(txport);
/* File Exists? Append It. File Non-Existent? Create It. */
if (!(fp=Open(BUFNAME,MODE_OLDFILE))) fp=Open(BUFNAME,MODE_NEWFILE);
/* tech note : There's no way of actually knowing when to close the disk
file, since we don't know when the user actually closes the parallel
device. We could patch the close vector to find out, but this works
just as well. */
Seek(fp,0,OFFSET_END); /* extend the file */
Write(fp,iomsg->IOPar.io_Data,iomsg->IOPar.io_Length); /* write buffer */
Close(fp); /* assume each write is the last; can always reopen later */
iomsg->IOPar.io_Flags=0; /* turn off QUICK_IO */
iomsg->IOPar.io_Error=0; /* no errors */
iomsg->IOPar.io_Actual=iomsg->IOPar.io_Length;
printf("%d bytes written from $%x.\n",
iomsg->IOPar.io_Length,iomsg->IOPar.io_Data);
ReplyMsg(iomsg); /* async I/O MUST reply */
}
}
/* our new BeginIO vector; intercepted CMD_WRITE cmds only! */
NewBeginSub(ioblock)
struct IOExtPar *ioblock;
{
/* the reason we're doing all this interprocess communication is because
there is NO guarantee that the task writing to the parallel.device is
a process! So, we send packets out to our process to get the job done.
Note : This code is re-entrant.
*/
PutMsg(txport,ioblock); /* signal our DOS process to write! */
return(0); /* return, no error in D0, ROM waiting for process to reply */
}